FÄ full typsÀkerhet och autokomplettering för JavaScript-bibliotek med TypeScript-deklarationsfiler. LÀr dig anvÀnda @types och skapa egna .d.ts-filer.
LÄs upp JavaScripts ekosystem: En djupdykning i TypeScript-deklarationsfiler
TypeScript har revolutionerat modern webbutveckling genom att införa statisk typning i den dynamiska JavaScript-vĂ€rlden. Denna typsĂ€kerhet ger otroliga fördelar: den fĂ„ngar fel vid kompilering, möjliggör kraftfull autokomplettering i kodredigerare och gör stora kodbaser betydligt mer underhĂ„llsbara. En stor utmaning uppstĂ„r dock nĂ€r vi vill anvĂ€nda det enorma ekosystemet av befintliga JavaScript-bibliotekâvarav de flesta inte Ă€r skrivna i TypeScript. Hur förstĂ„r vĂ„r strikt typade TypeScript-kod formerna, funktionerna och variablerna frĂ„n ett otypat JavaScript-bibliotek?
Svaret ligger i TypeScript-deklarationsfiler. Dessa filer, som kÀnns igen pÄ sin filÀndelse .d.ts, Àr den avgörande bryggan mellan TypeScript- och JavaScript-vÀrldarna. De fungerar som en ritning eller ett API-kontrakt som beskriver typerna i ett tredjepartsbibliotek utan att innehÄlla nÄgon av dess faktiska implementation. I denna omfattande guide kommer vi att utforska allt du behöver veta för att med sjÀlvförtroende hantera typdefinitioner för alla JavaScript-bibliotek i dina TypeScript-projekt.
Vad Àr TypeScript-deklarationsfiler egentligen?
FörestÀll dig att du har anlitat en entreprenör som bara talar ett annat sprÄk. För att arbeta effektivt med dem skulle du behöva en översÀttare eller en detaljerad uppsÀttning instruktioner pÄ ett sprÄk ni bÄda förstÄr. En deklarationsfil fyller exakt detta syfte för TypeScript-kompilatorn (entreprenören).
En .d.ts-fil innehÄller endast typinformation. Den inkluderar:
- Signaturer för funktioner och metoder (parametertyper, returtyper).
- Definitioner för variabler och deras typer.
- GrÀnssnitt (interfaces) och typalias för komplexa objekt.
- Klassdefinitioner, inklusive deras egenskaper och metoder.
- Namnrymds- och modulstrukturer.
Viktigt Àr att dessa filer inte innehÄller nÄgon körbar kod. De Àr enbart till för statisk analys. NÀr du importerar ett JavaScript-bibliotek som Lodash i ditt TypeScript-projekt letar kompilatorn efter en motsvarande deklarationsfil. Om den hittar en kan den validera din kod, ge intelligent autokomplettering och sÀkerstÀlla att du anvÀnder biblioteket korrekt. Om den inte hittar nÄgon kommer den att ge ett fel som: Could not find a declaration file for module 'lodash'.
Varför deklarationsfiler Àr oumbÀrliga för professionell utveckling
Att anvÀnda JavaScript-bibliotek utan korrekta typdefinitioner i ett TypeScript-projekt underminerar sjÀlva anledningen till att anvÀnda TypeScript frÄn första början. LÄt oss titta pÄ ett enkelt scenario med det populÀra hjÀlpbiblioteket Lodash.
VĂ€rlden utan typdefinitioner
Utan en deklarationsfil har TypeScript ingen aning om vad lodash Àr eller vad det innehÄller. För att ens fÄ koden att kompilera kan du frestas att anvÀnda en snabbfix som denna:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Autokomplettering? Ingen hjÀlp hÀr.
// Typkontroll? Nej. Ăr 'username' rĂ€tt egenskap?
// Kompilatorn tillÄter detta, men det kan krascha vid körning.
_.find(users, { username: 'fred' });
I det hÀr fallet Àr variabeln _ av typen any. Detta sÀger i praktiken till TypeScript, "Kontrollera ingenting som har med den hÀr variabeln att göra." Du förlorar alla fördelar: ingen autokomplettering, ingen typkontroll av argumenten och ingen sÀkerhet om returtypen. Detta Àr en grogrund för exekveringsfel (runtime errors).
VĂ€rlden med typdefinitioner
LÄt oss nu se vad som hÀnder nÀr vi tillhandahÄller den nödvÀndiga deklarationsfilen. Efter att ha installerat typerna (vilket vi kommer att gÄ igenom hÀrnÀst) förvandlas upplevelsen:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Kodredigeraren ger autokomplettering för 'find' och andra lodash-funktioner.
// 2. NÀr man hÄller muspekaren över 'find' visas dess fullstÀndiga signatur och dokumentation.
// 3. TypeScript ser att `users` Àr en array av `User`-objekt.
// 4. TypeScript vet att predikatet för `find` pÄ `User[]` bör involvera `user` eller `active`.
// KORREKT: TypeScript Àr nöjd.
const fred = _.find(users, { user: 'fred' });
// FEL: TypeScript fÄngar misstaget!
// Egenskapen 'username' finns inte pÄ typen 'User'.
const betty = _.find(users, { username: 'betty' });
Skillnaden Àr som natt och dag. Vi fÄr full typsÀkerhet, en överlÀgsen utvecklarupplevelse genom verktyg och en dramatisk minskning av potentiella buggar. Detta Àr den professionella standarden för att arbeta med TypeScript.
Hierarkin för att hitta typdefinitioner
SÄ, hur fÄr du tag pÄ dessa magiska .d.ts-filer för dina favoritbibliotek? Det finns en vÀletablerad process som tÀcker de allra flesta scenarier.
Steg 1: Kontrollera om biblioteket paketerar sina egna typer
Det bÀsta scenariot Àr nÀr ett bibliotek Àr skrivet i TypeScript eller dess underhÄllare tillhandahÄller officiella deklarationsfiler i samma paket. Detta blir allt vanligare för moderna, vÀl underhÄllna projekt.
Hur man kontrollerar:
- Installera biblioteket som vanligt:
npm install axios - Titta i bibliotekets mapp i
node_modules/axios. Ser du nÄgra.d.ts-filer? - Kontrollera bibliotekets
package.json-fil efter ett fÀlt som heter"types"eller"typings". Detta fÀlt pekar direkt till huvdeklarationsfilen. Till exempel innehÄller Axiospackage.json:"types": "index.d.ts".
Om dessa villkor Àr uppfyllda Àr du klar! TypeScript kommer automatiskt att hitta och anvÀnda dessa medföljande typer. Ingen ytterligare ÄtgÀrd krÀvs.
Steg 2: DefinitelyTyped-projektet (@types)
För de tusentals JavaScript-bibliotek som inte paketerar sina egna typer har det globala TypeScript-communityt skapat en otrolig resurs: DefinitelyTyped.
DefinitelyTyped Àr ett centraliserat, community-drivet repository pÄ GitHub som innehÄller högkvalitativa deklarationsfiler för ett enormt antal JavaScript-paket. Dessa definitioner publiceras till npm-registret under @types-scopet.
Hur man anvÀnder det:
Om ett bibliotek som lodash inte paketerar sina egna typer, installerar du helt enkelt dess motsvarande @types-paket som en utvecklingsberoende (development dependency):
npm install --save-dev @types/lodash
Namngivningskonventionen Àr enkel och förutsÀgbar: för ett paket som heter package-name, kommer dess typer nÀstan alltid att finnas pÄ @types/package-name. Du kan söka efter tillgÀngliga typer pÄ npm:s webbplats eller direkt pÄ DefinitelyTyped-repositoryt.
Varför --save-dev? Deklarationsfiler behövs bara under utveckling och kompilering. De innehÄller ingen exekveringskod, sÄ de ska inte inkluderas i din slutliga produktionsbundle. Att installera dem som en devDependency sÀkerstÀller denna separation.
Steg 3: NĂ€r inga typer finns â skriv dina egna
Vad hĂ€nder om du anvĂ€nder ett Ă€ldre, nischat eller internt privat bibliotek som inte paketerar typer och inte finns pĂ„ DefinitelyTyped? I det hĂ€r fallet mĂ„ste du kavla upp Ă€rmarna och skapa din egen deklarationsfil. Ăven om detta kan lĂ„ta skrĂ€mmande kan du börja enkelt och lĂ€gga till mer detaljer vid behov.
Snabbfixen: Kortfattad omgivande moduldeklaration
Ibland behöver du bara fÄ ditt projekt att kompilera utan fel medan du utarbetar en korrekt typningsstrategi. Du kan skapa en fil i ditt projekt (t.ex. declarations.d.ts eller types/global.d.ts) och lÀgga till en kortfattad deklaration:
// i en .d.ts-fil
declare module 'some-untyped-library';
Detta sÀger till TypeScript, "Lita pÄ mig, en modul med namnet 'some-untyped-library' existerar. Behandla bara allt som importeras frÄn den som typen any." Detta tystar kompileringsfelet, men som vi har diskuterat offrar det all typsÀkerhet för det biblioteket. Det Àr en tillfÀllig lösning, inte en lÄngsiktig.
Skapa en grundlÀggande egen deklarationsfil
En bÀttre metod Àr att börja definiera typerna för de delar av biblioteket du faktiskt anvÀnder. LÄt oss sÀga att vi har ett enkelt bibliotek som heter `string-utils` som exporterar en enda funktion.
// I node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
Vi kan skapa en fil string-utils.d.ts i en dedikerad `types`-katalog i vÄrt projekts rot.
// I mitt-projekt/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// Du kan lÀgga till andra funktionsdefinitioner hÀr nÀr du anvÀnder dem
// export function slugify(str: string): string;
}
Nu mÄste vi tala om för TypeScript var den kan hitta vÄra anpassade typdefinitioner. Vi gör detta i tsconfig.json:
{
"compilerOptions": {
// ... andra alternativ
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
Med denna konfiguration, nÀr du kör import { capitalize } from 'string-utils', kommer TypeScript att hitta din anpassade deklarationsfil och ge den typsÀkerhet du har definierat. Du kan gradvis bygga ut denna fil nÀr du anvÀnder fler funktioner i biblioteket.
Djupdykning: Skapa deklarationsfiler
LÄt oss utforska nÄgra mer avancerade koncept som du kommer att stöta pÄ nÀr du skriver eller lÀser deklarationsfiler.
Deklarera olika typer av exporter
JavaScript-moduler kan exportera saker pÄ olika sÀtt. Din deklarationsfil mÄste matcha bibliotekets exportstruktur.
- Namngivna exporter (Named Exports): Detta Àr det vanligaste. Vi sÄg det ovan med `export function capitalize(...)`. Du kan ocksÄ exportera konstanter, grÀnssnitt och klasser.
- Standardexport (Default Export): För bibliotek som anvÀnder `export default`.
- UMD-globaler: För Àldre bibliotek som Àr utformade för att fungera i webblÀsare via en
<script>-tagg, fÀster de sig ofta vid det globala `window`-objektet. Du kan deklarera dessa globala variabler. export =andimport = require(): Denna syntax Àr för Àldre CommonJS-moduler som anvÀnder `module.exports = ...`. Till exempel, om ett bibliotek gör `module.exports = myClass;`.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// För en funktion som standardexport
export default function myCoolFunction(): void;
// För ett objekt som standardexport
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Deklarerar en global variabel '$' av en viss typ
declare var $: JQueryStatic;
// i my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// i din app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
Ăven om det Ă€r mindre vanligt med moderna ES-moduler Ă€r detta avgörande för kompatibilitet med mĂ„nga Ă€ldre men fortfarande mycket anvĂ€nda Node.js-paket.
Modulutökning (Module Augmentation): Utöka befintliga typer
En av de mest kraftfulla funktionerna Àr modulutökning (Àven kÀnd som declaration merging). Detta gör att du kan lÀgga till egenskaper till befintliga grÀnssnitt som definierats i ett annat pakets deklarationsfil. Detta Àr extremt anvÀndbart för bibliotek med en plugin-arkitektur, som Express eller Fastify.
FörestÀll dig att du anvÀnder en middleware i Express som lÀgger till egenskapen `user` till `Request`-objektet. Utan utökning skulle TypeScript klaga pÄ att `user` inte finns pÄ `Request`.
SÄ hÀr kan du berÀtta för TypeScript om denna nya egenskap:
// i din types/express.d.ts-fil
// Vi mÄste importera originaltypen för att utöka den
import { UserProfile } from './auth'; // Förutsatt att du har en UserProfile-typ
// BerÀtta för TypeScript att vi utökar modulen 'express-serve-static-core'
declare module 'express-serve-static-core' {
// Rikta in oss pÄ 'Request'-grÀnssnittet i den modulen
interface Request {
// LÀgg till vÄr anpassade egenskap
user?: UserProfile;
}
}
Nu kommer Express Request-objektet i hela din applikation att vara korrekt typat med den valfria egenskapen `user`, och du fÄr full typsÀkerhet och autokomplettering.
Trippel-snedstreck-direktiv
Du kanske ibland ser kommentarer högst upp i .d.ts-filer som börjar med tre snedstreck (///). Dessa Àr trippel-snedstreck-direktiv, som fungerar som kompilatorinstruktioner.
/// <reference types="..." />: Detta Àr det vanligaste. Det inkluderar explicit ett annat pakets typdefinitioner som ett beroende. Till exempel kan typerna för ett WebdriverIO-plugin innehÄlla/// <reference types="webdriverio" />eftersom dess egna typer Àr beroende av de centrala WebdriverIO-typerna./// <reference path="..." />: Detta anvÀnds för att deklarera ett beroende till en annan fil inom samma projekt. Det Àr en Àldre syntax, som till stor del har ersatts av ES-modulimporter.
BÀsta praxis för hantering av deklarationsfiler
- Föredra paketerade typer: NÀr du vÀljer mellan bibliotek, favorisera de som Àr skrivna i TypeScript eller paketerar sina egna officiella typdefinitioner. Det signalerar ett engagemang för TypeScript-ekosystemet.
- BehÄll
@typesidevDependencies: Installera alltid@types-paket med--save-develler-D. De behövs inte för din produktionskod. - Synkronisera versioner: En vanlig felkÀlla Àr en oöverensstÀmmelse mellan biblioteksversionen och dess
@types-version. En större versionshöjning i ett bibliotek (t.ex. frÄn v2 till v3) kommer sannolikt att ha brytande Àndringar i sitt API, vilket mÄste Äterspeglas i@types-paketet. Försök att hÄlla dem synkroniserade. - AnvÀnd
tsconfig.jsonför kontroll: KompilatoralternativentypeRootsochtypesi dintsconfig.jsonkan ge dig finkornig kontroll över var TypeScript letar efter deklarationsfiler.typeRootstalar om för kompilatorn vilka mappar den ska kontrollera (som standard Àr det./node_modules/@types), ochtypeslÄter dig explicit lista vilka typpaket som ska inkluderas. - Bidra tillbaka: Om du skriver en omfattande deklarationsfil för ett bibliotek som saknar en, övervÀg att bidra med den till DefinitelyTyped-projektet. Detta Àr ett fantastiskt sÀtt att ge tillbaka till den globala utvecklargemenskapen och hjÀlpa tusentals andra.
Slutsats: TypsÀkerhetens obesjungna hjÀltar
TypeScript-deklarationsfiler Àr de obesjungna hjÀltar som gör det möjligt att sömlöst integrera den dynamiska, vidstrÀckta JavaScript-vÀrlden i en robust, typsÀker utvecklingsmiljö. De Àr den kritiska lÀnken som stÀrker vÄra verktyg, förhindrar otaliga buggar och gör vÄra kodbaser mer motstÄndskraftiga och sjÀlv-dokumenterande.
Genom att förstĂ„ hur man hittar, anvĂ€nder och till och med skapar dina egna .d.ts-filer, fixar du inte bara ett kompileringsfelâdu lyfter hela ditt utvecklingsflöde. Du lĂ„ser upp den fulla potentialen hos bĂ„de TypeScript och det rika ekosystemet av JavaScript-bibliotek, vilket skapar en kraftfull synergi som resulterar i bĂ€ttre och mer tillförlitlig programvara för en global publik.